﻿using System;
using System.Collections.Generic;
using System.Linq;
using Xenta.Attributes;
using Xenta.Enums;

namespace Xenta.Events
{
    [Dependency("0973dd27-a5ab-4ac3-85bc-333a7dbe2530", 
        Lifestyle = DependencyLifestyle.Singleton)]
    public sealed class DefaultEventBroker : IEventBroker
    {
        #region Fields

        private readonly List<_Subscription> _subscriptions;
        private readonly object _lock;

        #endregion

        #region Ctors

        public DefaultEventBroker()
        {
            _subscriptions = new List<_Subscription>();
            _lock = new Object();
        }

        #endregion

        #region Methods

        public void Publish<TEvent>(TEvent evt) 
            where TEvent : IEvent
        {
            if(Equals(evt, default(TEvent)))
                return;
            var handlers = Enumerable.Empty<Delegate>();
            lock(_lock)
            {
                if(!_subscriptions.Any())
                    return;
                var evtType = typeof(TEvent);
                var subscription = _subscriptions
                    .Where(x => x.Type == evtType)
                    .SingleOrDefault(x => 
                        evt.Marker == null || 
                        evt.Marker.Equals(x.Marker));
                if(subscription == null)
                    return;
                handlers = subscription.Handlers.ToArray();
            }
            if(!handlers.Any())
                return;
            foreach(var handler in handlers.Cast<Action<TEvent>>())
                handler(evt);
        }

        public void Subscribe<TEvent>(Action<TEvent> handler, object marker = null) 
            where TEvent : IEvent
        {
            if(handler == null)
                return;
            lock(_lock)
            {
                var evtType = typeof(TEvent);
                var subscription = _subscriptions
                    .Where(x => x.Type == evtType)
                    .SingleOrDefault(x =>
                        marker == null ||
                        marker.Equals(x.Marker));
                if(subscription == null)
                {
                    subscription = new _Subscription
                    {
                        Type = evtType,
                        Marker = marker,
                        Handlers = new List<Delegate>()
                    };
                    _subscriptions.Add(subscription);
                }
                subscription.Handlers.Add(handler);
            }
        }

        public void Unsubscribe<TEvent>(Action<TEvent> handler, object marker = null) 
            where TEvent : IEvent
        {
            if(handler == null)
                return;
            lock(_lock)
            {
                var evtType = typeof(TEvent);
                var subscription = _subscriptions
                    .Where(x => x.Type == evtType)
                    .SingleOrDefault(x =>
                        marker == null ||
                        marker.Equals(x.Marker));
                if(subscription == null)
                    return;
                subscription.Handlers.Remove(handler);
                if(!subscription.Handlers.Any())
                    _subscriptions.Remove(subscription);
            }
        }

        public void Dispose()
        {
            lock(_lock)
            {
                _subscriptions.Clear();
            }
        }

        #endregion

        #region Classes

        private class _Subscription
        {
            public Type Type
            {
                get;
                set;
            }

            public object Marker
            {
                get;
                set;
            }

            public ICollection<Delegate> Handlers
            {
                get;
                set;
            }
        }

        #endregion
    }
}
